package tool

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"git.900sui.cn/kc/kcgin"
	"net/url"
	"sort"
	"strings"
	"time"
)

type AliClient struct {
	AppId              	string	//支付宝appid
	Rsa2PrivateKey     	string	//支付宝RSA2应用私钥
	Rsa2PublicKey     	string	//支付宝RSA2应用公钥
	Cid					int		//区域id
	NotifyUrl			string	//回调url
}


func NewAliClient(cid int)( *AliClient, error ){
	if cid == CHANNELAREA {
		client := &AliClient{
			Cid :				CHANNELAREA,
			AppId: 				kcgin.AppConfig.String("pingan.shAliAppid"),
			Rsa2PrivateKey: 	kcgin.AppConfig.String("pingan.shAliRsa2PrivateKey"),
			Rsa2PublicKey: 		kcgin.AppConfig.String("pingan.shAliRsa2PunlicKey"),
			NotifyUrl: 			kcgin.AppConfig.String("pingan.shAliNotifyUrl"),
		}
		return client,nil
	}else {
		err := errors.New("当前城市未开通平安支付通道配置")
		return nil,err
	}
}

//获取支付宝请求参数
func (a *AliClient) GetQueryParams(BodyParams map[string]interface{},method string)(query []byte, err error) {
	pubParam :=make(map[string]interface{})
	pubParam["app_id"] = a.AppId
	pubParam["method"] = method
	pubParam["format"] = "JSON"
	pubParam["charset"] = "utf-8"
	pubParam["sign_type"] = "RSA2"
	pubParam["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
	pubParam["version"] = "1.0"
	pubParam["notify_url"] = a.NotifyUrl

	bodyBs, err := json.Marshal(BodyParams);
	if  err != nil {
		return nil, err
	}
	bodyStr := string(bodyBs)
	pubParam["biz_content"] = bodyStr
	sign,err := a.GetRsaSign(pubParam)
	if err != nil {
		return nil, nil
	}
	pubParam["sign"] = sign
	query = []byte(FormatURLParam(pubParam))
	return
}

//签名
func (a *AliClient)GetRsaSign(signMap map[string]interface{})(sign string,err error){

	pk := FormatPrivateKey(a.Rsa2PrivateKey)
	block, _ := pem.Decode([]byte(pk))
	if  block == nil {
		return "", errors.New("pem.Decode：privateKey decode error")
	}
	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}
	h := sha256.New()
	hashs := crypto.SHA256
	_, err = h.Write([]byte(EncodeAliPaySignParams(signMap)));
	if err != nil {
		return "", err
	}
	encryptedBytes, err := rsa.SignPKCS1v15(rand.Reader, key, hashs, h.Sum(nil))
	if err != nil {
		return "", err
	}
	sign = base64.StdEncoding.EncodeToString(encryptedBytes)
	return
}

type BodyMap map[string]interface{}

//将map排序拼接成字符串
func EncodeAliPaySignParams(bodyMap BodyMap) string {
	var (
		buf     strings.Builder
		keyList []string
	)
	for k := range bodyMap {
		keyList = append(keyList, k)
	}
	sort.Strings(keyList)
	for _, k := range keyList {
		if v := bodyMap.GetString(k); v != "" {
			buf.WriteString(k)
			buf.WriteByte('=')
			buf.WriteString(v)
			buf.WriteByte('&')
		}
	}
	if buf.Len() <= 0 {
		return ""
	}
	a := buf.String()[:buf.Len()-1]
	return a
}

// 获取map参数并将其转换string
func (bm BodyMap) GetString(key string) string {
	if bm == nil {
		return ""
	}
	value, ok := bm[key]
	if !ok {
		return ""
	}
	v, ok := value.(string)
	if !ok {
		return convertToString(value)
	}
	return v
}

//将数据转为string
func convertToString(v interface{}) (str string) {
	if v == nil {
		return ""
	}
	var (
		bs  []byte
		err error
	)
	if bs, err = json.Marshal(v); err != nil {
		return ""
	}
	str = string(bs)
	return
}

// 格式化请求URL参数
func FormatURLParam(body map[string]interface{}) (urlParam string) {
	v := url.Values{}
	for key, value := range body {
		v.Add(key, value.(string))
	}
	return v.Encode()
}

// 支付宝异步通知验签
func (a *AliClient) Notify(notifyMap map[string]interface{})(err error)  {
	bodySign := notifyMap["sign"]
	if _,ok := notifyMap["sign"].(string);ok {
		delete(notifyMap, "sign")
	}
	if _,ok := notifyMap["sign_type"].(string);ok {
		delete(notifyMap, "sign_type")
	}
	//特殊处理
	if _,ok := notifyMap["fund_bill_list"].(string);ok{
		if strings.Contains(notifyMap["fund_bill_list"].(string),"&quot;"){
			notifyMap["fund_bill_list"] = strings.Replace(notifyMap["fund_bill_list"].(string),"&quot;",`"`,-1)
		}
	}
	//\\特殊处理
	singData :=EncodeAliPaySignParams(notifyMap)
	err = a.verify(singData,bodySign.(string))
	if err != nil{
		return
	}
	return
}

func (a *AliClient) verify(signData,sign string)(err error)  {
	pk := FormatPublicKey(a.Rsa2PublicKey)
	signBytes, _ := base64.StdEncoding.DecodeString(sign)
	block, _ := pem.Decode([]byte(pk));
	if block == nil {
		return errors.New("支付宝公钥Decode错误")
	}
	pubKey, err := x509.ParsePKIXPublicKey(block.Bytes);
	if  err != nil {
		return fmt.Errorf("x509.ParsePKIXPublicKey error ：%s", err.Error())
	}
	publicKey, ok := pubKey.(*rsa.PublicKey);
	if !ok {
		return errors.New("支付宝公钥转换错误")
	}
	hashs := crypto.SHA256
	h := hashs.New()
	h.Write([]byte(signData))
	return rsa.VerifyPKCS1v15(publicKey, hashs, h.Sum(nil), signBytes)
}